import pytest
import allure
import requests
from typing import Optional, Dict, Any
from SelfStudy.FrameWork.ApiIhrmpytest.config import BASE_URL
from SelfStudy.FrameWork.ApiIhrmpytest.common.logger_util import get_logger
from SelfStudy.FrameWork.ApiIhrmpytest.common.request_util import RequestUtil
from SelfStudy.FrameWork.ApiIhrmpytest.common.assert_util import AssertUtil


log = get_logger()


class ApiUtil:
    """统一的API工具类，整合增删改查操作"""

    def __init__(self, base_url: str = BASE_URL, base_dir: Optional[str] = None, store: Optional[Dict] = None):
        """
        初始化API工具类
        :param base_url: 基础URL
        :param base_dir: 基础目录路径（用于读取测试数据）
        :param store: 存储测试数据的对象
        """
        self.base_url = base_url.rstrip("/")
        self.base_dir = base_dir
        self.store = store
        self._init_headers()

    def _init_headers(self) -> None:
        """初始化请求头"""
        if self.store:
            self.token = self.store.get("token")
            assert self.token, "没有从 store 中获取到 token"
            self.headers = {
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self.token}"
            }

    def _send_request(self, method: str, url: str, **kwargs) -> requests.Response:
        """
        发送HTTP请求的通用方法
        :param method: HTTP方法 (get, post, put, delete)
        :param url: 请求URL
        :param kwargs: 其他请求参数
        :return: 响应对象
        """
        try:
            log.info(f" 发送 {method.upper()} 请求: {url}")
            response = requests.request(method, url, headers=self.headers, **kwargs)
            self._log_response(response)
            self._attach_to_allure(url, kwargs.get('json'), response)
            return response
        except Exception as e:
            log.error(f"请求失败: {e}")
            raise

    def _log_response(self, response: requests.Response) -> None:
        """记录响应日志"""
        log.info(f"状态码: {response.status_code}")
        try:
            log.info(f"响应内容: {response.json()}")
        except ValueError:
            log.warning("响应不是合法JSON")

    def _attach_to_allure(self, url: str, request_data: Optional[Dict], response: requests.Response) -> None:
        """添加信息到Allure报告"""
        allure.attach(url, "请求URL", allure.attachment_type.TEXT)
        if request_data:
            allure.attach(str(request_data), "请求参数", allure.attachment_type.JSON)
        allure.attach(response.text, "响应内容", allure.attachment_type.JSON)

    def _handle_response(self, response: requests.Response, expected_status: int = 200) -> Dict:
        """
        处理响应数据
        :param response: 响应对象
        :param expected_status: 预期状态码
        :return: 响应JSON数据
        """
        assert response.status_code == expected_status, f"请求失败，状态码：{response.status_code}"
        try:
            return response.json()
        except ValueError as e:
            log.error(f"响应解析失败: {e}")
            pytest.fail("响应不是合法JSON")

    def _save_to_store(self, data_key: str, value: Any) -> None:
        """保存数据到store"""
        if self.store and data_key:
            self.store[data_key] = value
            log.info(f"成功保存 {data_key} 到 store")

    def add_resource(self, resource_type: str, data: Dict, id_key: Optional[str] = None,
                     expected: Optional[Dict] = None) -> Dict:
        """
        添加资源（新增 expected 参数）
        :param resource_type: 资源类型（如 'user', 'role'）
        :param data: 请求数据
        :param id_key: store中存储ID的键名（可选）
        :param expected: 预期结果（从YAML读取的expected字段）
        :return: 响应数据
        """
        url = f"{self.base_url}/api/sys/{resource_type}"
        log.info(f"\n====== 添加 {resource_type} ======")

        response = self._send_request('post', url, json=data)
        res_json = self._handle_response(response)

        # 如果有预期结果，则进行断言
        if expected:
            AssertUtil.assert_response(response, expected)

        if id_key and (data := res_json.get('data')) and isinstance(data, dict):
            self._save_to_store(id_key, data.get('id'))

        return res_json

    def update_resource(self, resource_type: str, obj_id: str, data: Dict) -> Dict:
        """
        修改资源
        :param resource_type: 资源类型
        :param obj_id: 资源ID
        :param data: 要修改的数据
        :return: 响应数据
        """
        url = f"{self.base_url}/api/sys/{resource_type}/{obj_id}"
        response = self._send_request('put', url, json=data)
        return self._handle_response(response)

    def delete_resource(self, resource_type: str, obj_id: str) -> Dict:
        """
        删除资源
        :param resource_type: 资源类型
        :param obj_id: 资源ID
        :return: 响应数据
        """
        url = f"{self.base_url}/api/sys/{resource_type}/{obj_id}"
        response = self._send_request('delete', url)
        return self._handle_response(response)

    def get_resource(self, resource_type: str, obj_id: str, data_key: Optional[str] = None) -> Dict:
        """
        获取资源
        :param resource_type: 资源类型
        :param obj_id: 资源ID
        :param data_key: 存储响应数据的键名（可选）
        :return: 响应数据
        """
        url = f"{self.base_url}/api/sys/{resource_type}/{obj_id}"
        response = self._send_request('get', url)
        res_json = self._handle_response(response)

        if data_key and (data := res_json.get('data')):
            self._save_to_store(data_key, data)

        return res_json